<?php

namespace PhpYes\Modules\Log;


final class Logger implements LoggerInterface
{
    /** @var array */
    private $config;
    /** @var string */
    private $logRecordClass;
    /** @var LoggerBackendInterface[] */
    private $backendArr;

    /**
     * Logger constructor.
     * @param array $config
     */
    public function __construct($config)
    {
        $this->config = $config;
        $this->logRecordClass = empty($config['record']) ? '' : trim($config['record']);
        $backendConfigArr = empty($config['backend']) ? null : $config['backend'];
        if ($backendConfigArr) {
            $this->backendArr = array();
            foreach ($backendConfigArr as $backendConfig) {
                $backendClass = empty($backendConfig['class']) ? null : trim($backendConfig['class']);
                if ($backendClass) {
                    $this->backendArr[] = new $backendClass($backendConfig);
                }
            }
        }
    }

    /**
     * @return array
     */
    public function getConfig()
    {
        return $this->config;
    }

    /**
     * @return string
     */
    public function getLogRecordClass()
    {
        return $this->logRecordClass;
    }

    /**
     * @return LoggerBackendInterface[]
     */
    public function getBackendArr()
    {
        return $this->backendArr;
    }

    /**
     * @param string $level - refer LogLevel
     * @param string $message
     * @return void
     */
    public function log($level, $message)
    {
        if (empty($this->backendArr)) {
            return;
        }
        $threshold = empty($this->config['threshold']) ? LogLevel::DEBUG : strtolower(trim($this->config['threshold']));
        $threshold = isset(LogLevel::$logLevel2Severity[$threshold]) ? LogLevel::$logLevel2Severity[$threshold] : LogLevel::$logLevel2Severity[LogLevel::DEBUG];
        $severity = empty($level) ? LogLevel::DEBUG : strtolower(trim($level));
        $severity = isset(LogLevel::$logLevel2Severity[$severity]) ? LogLevel::$logLevel2Severity[$severity] : LogLevel::$logLevel2Severity[LogLevel::DEBUG];
        // 按照 LogLevel::$logLevel2Severity, 越严重, $severity 越小
        if ($severity > $threshold) {
            return;
        }

        $logRecordClass = $this->logRecordClass;
        /** @var LogRecordInterface $logRecord */
        $logRecord = new $logRecordClass($this->config);
        $logRecord->log($level, $message);
        foreach ($this->backendArr as $backend) {
            $backend->logRecord($logRecord);
        }
    }

    /**
     * System is unusable.
     *
     * @param string $message
     *
     * @return void
     */
    public function emergency($message)
    {
        $this->log(LogLevel::EMERGENCY, $message);
    }

    /**
     * Action must be taken immediately.
     *
     * Example: Entire website down, database unavailable, etc. This should
     * trigger the SMS alerts and wake you up.
     *
     * @param string $message
     *
     * @return void
     */
    public function alert($message)
    {
        $this->log(LogLevel::ALERT, $message);
    }

    /**
     * Critical conditions.
     *
     * Example: Application component unavailable, unexpected exception.
     *
     * @param string $message
     *
     * @return void
     */
    public function critical($message)
    {
        $this->log(LogLevel::CRITICAL, $message);
    }

    /**
     * Runtime errors that do not require immediate action but should typically
     * be logged and monitored.
     *
     * @param string $message
     *
     * @return void
     */
    public function error($message)
    {
        $this->log(LogLevel::ERROR, $message);
    }

    /**
     * Exceptional occurrences that are not errors.
     *
     * Example: Use of deprecated APIs, poor use of an API, undesirable things
     * that are not necessarily wrong.
     *
     * @param string $message
     *
     * @return void
     */
    public function warning($message)
    {
        $this->log(LogLevel::WARNING, $message);
    }

    /**
     * Normal but significant events.
     *
     * @param string $message
     *
     * @return void
     */
    public function notice($message)
    {
        $this->log(LogLevel::NOTICE, $message);
    }

    /**
     * Interesting events.
     *
     * Example: User logs in, SQL logs.
     *
     * @param string $message
     *
     * @return void
     */
    public function info($message)
    {
        $this->log(LogLevel::INFO, $message);
    }

    /**
     * Detailed debug information.
     *
     * @param string $message
     *
     * @return void
     */
    public function debug($message)
    {
        $this->log(LogLevel::DEBUG, $message);
    }
}